package leetcode.editor.cn.dsa19_trie;
//设计一个使用单词列表进行初始化的数据结构，单词列表中的单词 互不相同 。 如果给出一个单词，请判定能否只将这个单词中一个字母换成另一个字母，使得所形成的新单
//词存在于你构建的字典中。 
//
// 实现 MagicDictionary 类：
// MagicDictionary() 初始化对象
// void buildDict(String[] dictionary) 使用字符串数组 dictionary 设定该数据结构，dictionary 中的字
//符串互不相同
// bool search(String searchWord) 给定一个字符串 searchWord ，判定能否只将字符串中 一个 字母换成另一个字母，使得
//所形成的新字符串能够与字典中的任一字符串匹配。如果可以，返回 true ；否则，返回 false 。
//
// 示例：
//输入
//["MagicDictionary", "buildDict", "search", "search", "search", "search"]
//[[], [["hello", "leetcode"]], ["hello"], ["hhllo"], ["hell"], ["leetcoded"]]
//输出
//[null, null, false, true, false, false]
//
//解释
//MagicDictionary magicDictionary = new MagicDictionary();
//magicDictionary.buildDict(["hello", "leetcode"]);
//magicDictionary.search("hello"); // 返回 False
//magicDictionary.search("hhllo"); // 将第二个 'h' 替换为 'e' 可以匹配 "hello" ，所以返回 True
//magicDictionary.search("hell"); // 返回 False
//magicDictionary.search("leetcoded"); // 返回 False
//
// 提示：
// 1 <= dictionary.length <= 100
// 1 <= dictionary[i].length <= 100
// dictionary[i] 仅由小写英文字母组成
// dictionary 中的所有字符串 互不相同
// 1 <= searchWord.length <= 100
// searchWord 仅由小写英文字母组成
// buildDict 仅在 search 之前调用一次
// 最多调用 100 次 search
//
// Related Topics 字典树 哈希表
// 👍 72 👎 0

public class ImplementMagicDictionary676 {
    //leetcode submit region begin(Prohibit modification and deletion)
     class TrieNode {
        private final int SIZE = 26; // 假设字典的元素都是小写字母
        TrieNode[] child; // 存储子结点
        boolean isWord; // 标记当前元素是否是单词的结尾
        public TrieNode() {
            isWord = false; // 初始化为false
            child = new TrieNode[SIZE]; // 子结点初始化为空
        }
    }
    class MagicDictionary {
        TrieNode root;
        /** Initialize your data structure here. */
        public MagicDictionary() {
            root = new TrieNode();
        }

        public void buildDict(String[] dictionary) {
            TrieNode presentNode = null;
            for (String word : dictionary) {
                presentNode = root;
                for (int i = 0; i < word.length(); i++) {
                    int wordPos = word.charAt(i) - 'a';
                    if (presentNode.child[wordPos] == null) {
                        presentNode.child[wordPos] = new TrieNode();
                    }
                    presentNode = presentNode.child[wordPos];
                }
                presentNode.isWord = true;
            }
        }

        public boolean search(String searchWord) {
            TrieNode presentNode = root;
            for (int i = 0; i < searchWord.length(); i++) { // 遍历待搜索字符
                int wordPos = searchWord.charAt(i) - 'a'; // 计算位置
                // 每次都遍历当前节点的所有子元素，看是否能有一个子节点可以替换当前字符
                for (int j = 0; j < 26; j++) {
                    // 如果子节点值是当前字符或者是空节点，则跳过
                    if (j == wordPos || presentNode.child[j] == null) continue;
                    // 否则以当前节点为根，搜索trie中是否包含剩余的所有字符
                    // 如果包含，说明替换当前节点的字母后的新单词存在trie中，返回true
                    if (partSearch(presentNode.child[j], searchWord, i+1))
                        return true;
                }
                // 当前字符替换失败，那么该字符必须存在trie中，否则返回false
                if (presentNode.child[wordPos] == null) return false;
                presentNode = presentNode.child[wordPos];
            }
            return false;
        }
        public boolean partSearch(TrieNode root, String searchWord, int start) {
            TrieNode presentNode = root;
            for (int i = start; i < searchWord.length(); i++) {
                int wordPos = searchWord.charAt(i) - 'a';
                if (presentNode.child[wordPos] == null) {
                    return false;
                }
                presentNode = presentNode.child[wordPos];
            }
            return presentNode.isWord;
        }
    }
    /**
     * Your MagicDictionary object will be instantiated and called as such:
     * MagicDictionary obj = new MagicDictionary();
     * obj.buildDict(dictionary);
     * boolean param_2 = obj.search(searchWord);
     */
    //leetcode submit region end(Prohibit modification and deletion)
}